package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"mariadb-operator/pkg/apis/finupgroup/v1alpha1"
	"mariadb-operator/pkg/constants"
	"os"
	"os/exec"
	"runtime"

	"github.com/operator-framework/operator-sdk/pkg/sdk"
	"github.com/sirupsen/logrus"
	"github.com/streadway/amqp"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func printVersion() {
	flag.Parse()
	logrus.Infof("Go Version: %s", runtime.Version())
	logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)
}

func failOnError(err error, msg string) {
	if err != nil {
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

var mq = flag.String("mq", "", "--mq=value")

func main() {
	defer func() {
		if err := recover(); err != nil {
			logrus.Error(err)
		}
	}()

	printVersion()
	if *mq == "" {
		mqRUL := os.Getenv("RABBIT_MQ")
		if mqRUL == "" {
			panic("mq url must be define, you can set the RABBIT_MQ env or -mq mqurl")
		}
		*mq = mqRUL
	}
	conn, err := amqp.Dial(*mq)
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	q, err := ch.QueueDeclare(
		constants.RestoreTaskQueueName, // name
		false, // durable
		false, // delete when usused
		false, // exclusive
		false, // no-wait
		nil,   // arguments
	)

	failOnError(err, "Failed to declare a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	forever := make(chan bool)

	go func() {
		for {
			select {
			case d := <-msgs:
				logrus.Printf("Received a message: %s", d.Body)
				task := &v1alpha1.RestoreTask{}
				if err := json.Unmarshal(d.Body, task); err != nil {
					logrus.Errorf("unmarshal task data to restoreTask struct err : %v", err)
					continue
				}

				status := v1alpha1.RestoreStatus{}
				status.TimeStarted = metav1.Now()
				condition := v1alpha1.RestoreCondition{
					Type: v1alpha1.RestoreRunning,
				}

				r := &v1alpha1.Restore{
					TypeMeta:   metav1.TypeMeta{APIVersion: "finupgroup.com/v1alpha1", Kind: "Restore"},
					ObjectMeta: metav1.ObjectMeta{Name: task.RestoreName, Namespace: task.RestoreNamespace},
				}
				err := sdk.Get(r)
				if err != nil {
					logrus.Errorf("get mariadb restore %v failed : %v", task.RestoreName, err)
					condition.LastTransitionTime = metav1.Now()
					condition.Message = err.Error()
					condition.Reason = err.Error()
					condition.Type = v1alpha1.RestoreFailed
					status.Conditions = []v1alpha1.RestoreCondition{condition}
					r.Status = status
					sdk.Update(r)
					continue
				}

				sdk.Update(r)

				err = restore(task)
				if err != nil {
					logrus.Error(err)
					condition.LastTransitionTime = metav1.Now()
					condition.Message = err.Error()
					condition.Reason = err.Error()
					condition.Type = v1alpha1.RestoreFailed
					status.Conditions = []v1alpha1.RestoreCondition{condition}
					r.Status = status
					sdk.Update(r)
					continue
				}
				condition.LastTransitionTime = metav1.Now()
				status.TimeCompleted = metav1.Now()
				condition.Type = v1alpha1.RestoreComplete
				status.Conditions = []v1alpha1.RestoreCondition{condition}
				r.Status = status
				sdk.Update(r)
				logrus.Infof("restore cluster %v data success", task.ClusterName)
			}
		}
	}()

	logrus.Printf(" [*] Waiting for restore task message. To exit press CTRL+C")
	<-forever
}

/*
backup steps:
1. download the data to restore
2. exec restore command to restore data
*/
func restore(task *v1alpha1.RestoreTask) error {
	// exec backup cmd
	c := exec.Command("bash", "-c", task.Command)
	_, err := c.CombinedOutput()
	if err != nil {
		return fmt.Errorf("exec backup cmd %v err : %v", task.Command, err)
	}

	logrus.Infof("restore cluster %v data success", task.ClusterName)
	os.RemoveAll(task.FileName)
	return nil
}
